<!DOCTYPE html>
<html lang="zh-cn">

    <head>
    <meta charset="utf-8">
    <!--
        © Material Theme
        https://github.com/viosey/hexo-theme-material
        Version: 1.5.2 -->
    <script>
        window.materialVersion = "1.5.2"
        // Delete localstorage with these tags
        window.oldVersion = [
            'codestartv1',
            '1.3.4',
            '1.4.0',
            '1.4.0b1',
            '1.5.0'
        ]
    </script>

    <!-- dns prefetch -->
    <meta http-equiv="x-dns-prefetch-control" content="on">




    <link rel="dns-prefetch" href="https://changyan.sohu.com"/>











    <!-- Meta & Info -->
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta name="renderer" content="webkit">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

    <!-- Title -->
    
    <title>
        
            React, Typescript构建Upload组件 | 
        
        春日游, 杏花吹满头
    </title>

    <!-- Favicons -->
    <link rel="icon shortcut" type="image/ico" href="/labike/img/avatar.png">
    <link rel="icon" href="/labike/img/avatar.png">

    <meta name="format-detection" content="telephone=no"/>
    <meta name="description" itemprop="description" content="">
    <meta name="keywords" content=",前端">
    <meta name="theme-color" content="#0097A7">

    <!-- Disable Fucking Bloody Baidu Tranformation -->
    <meta http-equiv="Cache-Control" content="no-transform" />
    <meta http-equiv="Cache-Control" content="no-siteapp" />

    <!--[if lte IE 9]>
        <link rel="stylesheet" href="/css/ie-blocker.css">

        
            <script src="/js/ie-blocker.en.js"></script>
        
    <![endif]-->

    <!-- Import lsloader -->
    <script>(function(){window.lsloader={jsRunSequence:[],jsnamemap:{},cssnamemap:{}};lsloader.removeLS=function(a){try{localStorage.removeItem(a)}catch(b){}};lsloader.setLS=function(a,c){try{localStorage.setItem(a,c)}catch(b){}};lsloader.getLS=function(a){var c="";try{c=localStorage.getItem(a)}catch(b){c=""}return c};versionString="/*"+(window.materialVersion||"unknownVersion")+"*/";lsloader.clean=function(){try{var b=[];for(var a=0;a<localStorage.length;a++){b.push(localStorage.key(a))}b.forEach(function(e){var f=lsloader.getLS(e);if(window.oldVersion){var d=window.oldVersion.reduce(function(g,h){return g||f.indexOf("/*"+h+"*/")!==-1},false);if(d){lsloader.removeLS(e)}}})}catch(c){}};lsloader.clean();lsloader.load=function(f,a,b,d){if(typeof b==="boolean"){d=b;b=undefined}d=d||false;b=b||function(){};var e;e=this.getLS(f);if(e&&e.indexOf(versionString)===-1){this.removeLS(f);this.requestResource(f,a,b,d);return}if(e){var c=e.split(versionString)[0];if(c!=a){console.log("reload:"+a);this.removeLS(f);this.requestResource(f,a,b,d);return}e=e.split(versionString)[1];if(d){this.jsRunSequence.push({name:f,code:e});this.runjs(a,f,e)}else{document.getElementById(f).appendChild(document.createTextNode(e));b()}}else{this.requestResource(f,a,b,d)}};lsloader.requestResource=function(b,e,a,c){var d=this;if(c){this.iojs(e,b,function(h,f,g){d.setLS(f,h+versionString+g);d.runjs(h,f,g)})}else{this.iocss(e,b,function(f){document.getElementById(b).appendChild(document.createTextNode(f));d.setLS(b,e+versionString+f)},a)}};lsloader.iojs=function(d,b,g){var a=this;a.jsRunSequence.push({name:b,code:""});try{var f=new XMLHttpRequest();f.open("get",d,true);f.onreadystatechange=function(){if(f.readyState==4){if((f.status>=200&&f.status<300)||f.status==304){if(f.response!=""){g(d,b,f.response);return}}a.jsfallback(d,b)}};f.send(null)}catch(c){a.jsfallback(d,b)}};lsloader.iocss=function(f,c,h,a){var b=this;try{var g=new XMLHttpRequest();g.open("get",f,true);g.onreadystatechange=function(){if(g.readyState==4){if((g.status>=200&&g.status<300)||g.status==304){if(g.response!=""){h(g.response);a();return}}b.cssfallback(f,c,a)}};g.send(null)}catch(d){b.cssfallback(f,c,a)}};lsloader.iofonts=function(f,c,h,a){var b=this;try{var g=new XMLHttpRequest();g.open("get",f,true);g.onreadystatechange=function(){if(g.readyState==4){if((g.status>=200&&g.status<300)||g.status==304){if(g.response!=""){h(g.response);a();return}}b.cssfallback(f,c,a)}};g.send(null)}catch(d){b.cssfallback(f,c,a)}};lsloader.runjs=function(f,c,e){if(!!c&&!!e){for(var b in this.jsRunSequence){if(this.jsRunSequence[b].name==c){this.jsRunSequence[b].code=e}}}if(!!this.jsRunSequence[0]&&!!this.jsRunSequence[0].code&&this.jsRunSequence[0].status!="failed"){var a=document.createElement("script");a.appendChild(document.createTextNode(this.jsRunSequence[0].code));a.type="text/javascript";document.getElementsByTagName("head")[0].appendChild(a);this.jsRunSequence.shift();if(this.jsRunSequence.length>0){this.runjs()}}else{if(!!this.jsRunSequence[0]&&this.jsRunSequence[0].status=="failed"){var d=this;var a=document.createElement("script");a.src=this.jsRunSequence[0].path;a.type="text/javascript";this.jsRunSequence[0].status="loading";a.onload=function(){d.jsRunSequence.shift();if(d.jsRunSequence.length>0){d.runjs()}};document.body.appendChild(a)}}};lsloader.tagLoad=function(b,a){this.jsRunSequence.push({name:a,code:"",path:b,status:"failed"});this.runjs()};lsloader.jsfallback=function(c,b){if(!!this.jsnamemap[b]){return}else{this.jsnamemap[b]=b}for(var a in this.jsRunSequence){if(this.jsRunSequence[a].name==b){this.jsRunSequence[a].code="";this.jsRunSequence[a].status="failed";this.jsRunSequence[a].path=c}}this.runjs()};lsloader.cssfallback=function(e,c,b){if(!!this.cssnamemap[c]){return}else{this.cssnamemap[c]=1}var d=document.createElement("link");d.type="text/css";d.href=e;d.rel="stylesheet";d.onload=d.onerror=b;var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(d,a)};lsloader.runInlineScript=function(c,b){var a=document.getElementById(b).innerText;this.jsRunSequence.push({name:c,code:a});this.runjs()}})();</script>

    <!-- Import queue -->
    <script>function Queue(){this.dataStore=[];this.offer=b;this.poll=d;this.execNext=a;this.debug=false;this.startDebug=c;function b(e){if(this.debug){console.log("Offered a Queued Function.")}if(typeof e==="function"){this.dataStore.push(e)}else{console.log("You must offer a function.")}}function d(){if(this.debug){console.log("Polled a Queued Function.")}return this.dataStore.shift()}function a(){var e=this.poll();if(e!==undefined){if(this.debug){console.log("Run a Queued Function.")}e()}}function c(){this.debug=true}}var queue=new Queue();</script>

    <!-- Import CSS -->
    
        <style id="material_css"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("material_css","/labike/css/material.min.css?Z7a72R1E4SxzBKR/WGctOA==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>
        <style id="style_css"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("style_css","/labike/css/style.min.css?MKetZV3cUTfDxvMffaOezg==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>

        
            
                <style id="prettify_css"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("prettify_css","/labike/css/prettify.min.css?zp8STOU9v89XWFEnN+6YmQ==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>
                <style id="prettify_theme"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("prettify_theme","/labike/css/prettify/github-v2.min.css?AfzKxt++K+/lhZBlSjnxwg==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>
            
        

    

    

    <!-- Config CSS -->

<!-- Other Styles -->
<style>
  body, html {
    font-family: Roboto, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
    overflow-x: hidden !important;
  }
  
  code {
    font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
  }

  a {
    color: #00838F;
  }

  .mdl-card__media,
  #search-label,
  #search-form-label:after,
  #scheme-Paradox .hot_tags-count,
  #scheme-Paradox .sidebar_archives-count,
  #scheme-Paradox .sidebar-colored .sidebar-header,
  #scheme-Paradox .sidebar-colored .sidebar-badge{
    background-color: #0097A7 !important;
  }

  /* Sidebar User Drop Down Menu Text Color */
  #scheme-Paradox .sidebar-colored .sidebar-nav>.dropdown>.dropdown-menu>li>a:hover,
  #scheme-Paradox .sidebar-colored .sidebar-nav>.dropdown>.dropdown-menu>li>a:focus {
    color: #0097A7 !important;
  }

  #post_entry-right-info,
  .sidebar-colored .sidebar-nav li:hover > a,
  .sidebar-colored .sidebar-nav li:hover > a i,
  .sidebar-colored .sidebar-nav li > a:hover,
  .sidebar-colored .sidebar-nav li > a:hover i,
  .sidebar-colored .sidebar-nav li > a:focus i,
  .sidebar-colored .sidebar-nav > .open > a,
  .sidebar-colored .sidebar-nav > .open > a:hover,
  .sidebar-colored .sidebar-nav > .open > a:focus,
  #ds-reset #ds-ctx .ds-ctx-entry .ds-ctx-head a {
    color: #0097A7 !important;
  }

  .toTop {
    background: #757575 !important;
  }

  .material-layout .material-post>.material-nav,
  .material-layout .material-index>.material-nav,
  .material-nav a {
    color: #757575;
  }

  #scheme-Paradox .MD-burger-layer {
    background-color: #757575;
  }

  #scheme-Paradox #post-toc-trigger-btn {
    color: #757575;
  }

  .post-toc a:hover {
    color: #00838F;
    text-decoration: underline;
  }

</style>


<!-- Theme Background Related-->

    <style>
      body{
        background-color: #F5F5F5;
      }

      /* blog_info bottom background */
      #scheme-Paradox .material-layout .something-else .mdl-card__supporting-text{
        background-color: #fff;
      }
    </style>




<!-- Fade Effect -->

    <style>
      .fade {
        transition: all 800ms linear;
        -webkit-transform: translate3d(0,0,0);
        -moz-transform: translate3d(0,0,0);
        -ms-transform: translate3d(0,0,0);
        -o-transform: translate3d(0,0,0);
        transform: translate3d(0,0,0);
        opacity: 1;
      }

      .fade.out{
        opacity: 0;
      }
    </style>


<!-- Import Font -->
<!-- Import Roboto -->

    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500" rel="stylesheet">


<!-- Import Material Icons -->


    <style id="material_icons"></style><script>if(typeof window.lsLoadCSSMaxNums === "undefined")window.lsLoadCSSMaxNums = 0;window.lsLoadCSSMaxNums++;lsloader.load("material_icons","/labike/css/material-icons.css?pqhB/Rd/ab0H2+kZp0RDmw==",function(){if(typeof window.lsLoadCSSNums === "undefined")window.lsLoadCSSNums = 0;window.lsLoadCSSNums++;if(window.lsLoadCSSNums == window.lsLoadCSSMaxNums)document.documentElement.style.display="";}, false)</script>




    <!-- Import jQuery -->
    
        <script>lsloader.load("jq_js","/labike/js/jquery.min.js?ezyEvm8ST5CGfpA+kFFi1g==", true)</script>
    

    <!-- WebAPP Icons -->
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="application-name" content="春日游, 杏花吹满头">
    <meta name="msapplication-starturl" content="http://labike.gitee.io/labike/2020/11/03/uploadComponent/">
    <meta name="msapplication-navbutton-color" content="#0097A7">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-title" content="春日游, 杏花吹满头">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <link rel="apple-touch-icon" href="/labike/img/avatar.png">

    <!-- Site Verification -->
    
    

    <!-- RSS -->
    

    <!-- The Open Graph protocol -->
    <meta property="og:url" content="http://labike.gitee.io/labike/2020/11/03/uploadComponent/">
    <meta property="og:type" content="blog">
    <meta property="og:title" content="React, Typescript构建Upload组件 | 春日游, 杏花吹满头">
    <meta property="og:image" content="/labike/img/avatar.png">
    <meta property="og:description" content="">
    <meta property="og:article:tag" content="前端"> 

    
        <meta property="article:published_time" content="Tue Nov 03 2020 16:16:39 GMT+0800">
        <meta property="article:modified_time" content="Wed Nov 04 2020 10:19:44 GMT+0800">
    

    <!-- The Twitter Card protocol -->
    <meta name="twitter:card" content="summary_large_image">

    <!-- Add canonical link for SEO -->
    
        <link rel="canonical" href="http://labike.gitee.io/labike/2020/11/03/uploadComponent/index.html" />
    

    <!-- Structured-data for SEO -->
    
        


<script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "mainEntityOfPage": "http://labike.gitee.io/labike/2020/11/03/uploadComponent/index.html",
    "headline": "React, Typescript构建Upload组件",
    "datePublished": "Tue Nov 03 2020 16:16:39 GMT+0800",
    "dateModified": "Wed Nov 04 2020 10:19:44 GMT+0800",
    "author": {
        "@type": "Person",
        "name": "labike",
        "image": {
            "@type": "ImageObject",
            "url": "/img/avatar.png"
        },
        "description": "Hi, nice to meet you"
    },
    "publisher": {
        "@type": "Organization",
        "name": "春日游, 杏花吹满头",
        "logo": {
            "@type":"ImageObject",
            "url": "/img/avatar.png"
        }
    },
    "keywords": ",前端",
    "description": "",
}
</script>


    

    <!-- Analytics -->
    
    
    

    <!-- Custom Head -->
    

<meta name="generator" content="Hexo 5.2.0"></head>


    
        <body id="scheme-Paradox" class="lazy">
            <div class="material-layout  mdl-js-layout has-drawer is-upgraded">
                

                <!-- Main Container -->
                <main class="material-layout__content" id="main">

                    <!-- Top Anchor -->
                    <div id="top"></div>

                    
                        <!-- Hamburger Button -->
                        <button class="MD-burger-icon sidebar-toggle">
                            <span id="MD-burger-id" class="MD-burger-layer"></span>
                        </button>
                    

                    <!-- Post TOC -->

    
    <!-- Back Button -->
    <!--
    <div class="material-back" id="backhome-div" tabindex="0">
        <a class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon"
           href="#" onclick="window.history.back();return false;"
           target="_self"
           role="button"
           data-upgraded=",MaterialButton,MaterialRipple">
            <i class="material-icons" role="presentation">arrow_back</i>
            <span class="mdl-button__ripple-container">
                <span class="mdl-ripple"></span>
            </span>
        </a>
    </div>
    -->


    <!-- Left aligned menu below button -->
    
    
    <button id="post-toc-trigger-btn"
        class="mdl-button mdl-js-button mdl-button--icon">
        <i class="material-icons">format_list_numbered</i>
    </button>

    <ul class="post-toc-wrap mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effect" for="post-toc-trigger-btn" style="max-height:80vh; overflow-y:scroll;">
        <ol class="post-toc"><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#%E5%9F%BA%E6%9C%AC%E7%89%88"><span class="post-toc-number">1.</span> <span class="post-toc-text">基本版</span></a></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#%E4%BD%BF%E7%94%A8Typescript%E5%A3%B0%E6%98%8E%E5%9F%BA%E6%9C%ACprops"><span class="post-toc-number">2.</span> <span class="post-toc-text">使用Typescript声明基本props</span></a></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#%E5%8D%87%E7%BA%A7Upload"><span class="post-toc-number">3.</span> <span class="post-toc-text">升级Upload</span></a></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#%E5%86%8D%E4%B8%8A%E4%B8%80%E4%B8%AA%E7%AD%89%E5%9C%BE"><span class="post-toc-number">4.</span> <span class="post-toc-text">再上一个等圾</span></a></li><li class="post-toc-item post-toc-level-4"><a class="post-toc-link" href="#%E6%8B%96%E6%8B%BD%E4%B8%8A%E4%BC%A0"><span class="post-toc-number">5.</span> <span class="post-toc-text">拖拽上传</span></a></li></ol>
    </ul>
    




<!-- Layouts -->

    <!-- Post Module -->
    <div class="material-post_container">

        <div class="material-post mdl-grid">
            <div class="mdl-card mdl-shadow--4dp mdl-cell mdl-cell--12-col">

                <!-- Post Header(Thumbnail & Title) -->
                
    <!-- Paradox Post Header -->
    
        
            <!-- Random Thumbnail -->
            <div class="post_thumbnail-random mdl-card__media mdl-color-text--grey-50">
            <script type="text/ls-javascript" id="post-thumbnail-script">
    var randomNum = Math.floor(Math.random() * 19 + 1);

    $('.post_thumbnail-random').attr('data-original', '/labike/img/random/material-' + randomNum + '.png');
    $('.post_thumbnail-random').addClass('lazy');
</script>

        
    
            <p class="article-headline-p">
                React, Typescript构建Upload组件
            </p>
        </div>





                
                    <!-- Paradox Post Info -->
                    <div class="mdl-color-text--grey-700 mdl-card__supporting-text meta">

    <!-- Author Avatar -->
    <div id="author-avatar">
        <img src="/labike/img/avatar.png" width="44px" height="44px" alt="Author Avatar"/>
    </div>
    <!-- Author Name & Date -->
    <div>
        <strong>labike</strong>
        <span>11月 03, 2020</span>
    </div>

    <div class="section-spacer"></div>

    <!-- Favorite -->
    <!--
        <button id="article-functions-like-button" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon btn-like">
            <i class="material-icons" role="presentation">favorite</i>
            <span class="visuallyhidden">favorites</span>
        </button>
    -->

    <!-- Qrcode -->
    

    <!-- Tags (bookmark) -->
    
    <button id="article-functions-viewtags-button" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon">
        <i class="material-icons" role="presentation">bookmark</i>
        <span class="visuallyhidden">bookmark</span>
    </button>
    <ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" for="article-functions-viewtags-button">
        <li class="mdl-menu__item">
        <a class="post_tag-none-link" href="/labike/tags/%E5%89%8D%E7%AB%AF/" rel="tag">前端</a>
    </ul>
    

    <!-- Share -->
    
        <button id="article-fuctions-share-button" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon">
    <i class="material-icons" role="presentation">share</i>
    <span class="visuallyhidden">share</span>
</button>
<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" for="article-fuctions-share-button">
    

    

    <!-- Share Weibo -->
    
        <a class="post_share-link" href="http://service.weibo.com/share/share.php?appkey=&title=React, Typescript构建Upload组件&url=http://labike.gitee.io/labike/2020/11/03/uploadComponent/index.html&pic=http://labike.gitee.io/img/avatar.png&searchPic=false&style=simple" target="_blank">
            <li class="mdl-menu__item">
                分享到微博
            </li>
        </a>
    

    <!-- Share Twitter -->
    
        <a class="post_share-link" href="https://twitter.com/intent/tweet?text=React, Typescript构建Upload组件&url=http://labike.gitee.io/labike/2020/11/03/uploadComponent/index.html&via=labike" target="_blank">
            <li class="mdl-menu__item">
                分享到 Twitter
            </li>
        </a>
    

    <!-- Share Facebook -->
    
        <a class="post_share-link" href="https://www.facebook.com/sharer/sharer.php?u=http://labike.gitee.io/labike/2020/11/03/uploadComponent/index.html" target="_blank">
            <li class="mdl-menu__item">
                分享到 Facebook
            </li>
        </a>
    

    <!-- Share Google+ -->
    
        <a class="post_share-link" href="https://plus.google.com/share?url=http://labike.gitee.io/labike/2020/11/03/uploadComponent/index.html" target="_blank">
            <li class="mdl-menu__item">
                分享到 Google+
            </li>
        </a>
    

    <!-- Share LinkedIn -->
    

    <!-- Share QQ -->
    

    <!-- Share Telegram -->
    
</ul>

    
</div>

                

                <!-- Post Content -->
                <div id="post-content" class="mdl-color-text--grey-700 mdl-card__supporting-text fade out">
    
        <p>在平常的业务开发中, 我们都会借助一些开源的组件库加速开发速度, 开源市场中比较知名的像Ant Design React/Vue, Element-ui都已经为我们提供了非常成熟完善的组件, 但我们开发时不仅要知道组件如何使用同时要明白组件背后的原理为自己封装组件提供技术积累.</p>
<p>这篇文章将用React和Typescript来构建一个和Ant Design React/Vue Upload组件大似相同的Upload组件来帮助你理解如何构建组件!</p>
<p>首先我们从最基本的上传组件开始.创建<code>Upload</code>组件.</p>
<h4 id="基本版"><a href="#基本版" class="headerlink" title="基本版"></a>基本版</h4><pre><code>&lt;input type=&quot;file&quot; /&gt;</code></pre>
<p>我们最基本的<code>Upload</code>组件只需要一个<code>input</code>将其<code>type</code>设置为<code>file</code>即可. 这非常简单同时非常丑陋而且提供的信息不够, 像Ant Design Upload组件有上传状态, 样式好看, 操作事件完整还提供拖拽功能, 那么我们如何在基本版上构建一个这样的组件呢, 接下来我们一步一步完善.</p>
<h4 id="使用Typescript声明基本props"><a href="#使用Typescript声明基本props" class="headerlink" title="使用Typescript声明基本props"></a>使用Typescript声明基本props</h4><p>首先我们为上传文件不同阶段提供不同的状态, 比如正在上传中uploading, 成功success, 报错error等等</p>
<pre><code>type UploadFileStatus = &#39;ready&#39; | &#39;uploading&#39; | &#39;success&#39; | &#39;error&#39;</code></pre>
<p>同时上传文件需要自定义自己的属性, 像文件id, 文件名, 文件大小等这些属性将在后续使用</p>
<pre><code>interface UploadFile &#123;
  uid: string;
  size: string;
  name: string;
&#125;</code></pre>
<p>接着我们声明<code>Upload</code>组件必须的属性</p>
<pre><code>interface UploadProps &#123;
  action: string;
  defaultFileList?: UploadFile[];
  beforeUpload?: (file: UploadFile) =&gt; boolean | Promise&lt;File&gt;;
  onSuccess?: (data: any, file: UploadFile) =&gt; void;
  onError?: (data: any, file: UploadFile) =&gt; void;
  onChange?: (file: UploadFile) =&gt; void;
  name?: string;
  data: &#123;[key:string]: any&#125;;
  multiple?: boolean;
  accept?: string
&#125;</code></pre>
<p><code>action</code>为上传地址, <code>defaultFileList</code>表示上传可能是单个也可能是多个, <code>beforeUpload</code>上传之前的处理函数, <code>onSuccess, onError</code>成功失败处理函数, <code>onChange</code>上传文件改变的时候触发的函数等等</p>
<h4 id="升级Upload"><a href="#升级Upload" class="headerlink" title="升级Upload"></a>升级Upload</h4><p>这里我们不直接使用<code>input</code>而是借助一个<code>button</code>按钮来触发上传操作</p>
<pre><code>import React, &#123;useRef, HTMLInputElement&#125; from &#39;react&#39;

const Upload: React.FC&lt;UploadProps&gt; = (props) =&gt; &#123;
  const &#123;
    action,
    defaultFileList,
    beforeUpload,
    onSuccess,
    onError,
    onChange,
    name,
    data,
    accept,
    multiple
  &#125; = props

  // 获取input实例
  const fileInputRef = useRef&lt;HTMLInputElement&gt;(null)
  // 点击button时其实就是触发了input
  const handleClick = () =&gt; &#123;
   if (inputFileRef.current) &#123;
     inputFileRef.current.click()
   &#125;
  &#125;

  const handleChangeFile = (e: React.ChangeEvent&lt;HTMLInputHTML&gt;) =&gt; &#123;
    const files = e.target.files
    if (!files) return
    uploadFiles(files)
    // 清空上传文件
    if (fileInputRef.current) &#123;
      fileInput.current.value = &#39;&#39;
    &#125;
  &#125;

  const uploadFiles = (files: FileList) =&gt; &#123;
    let postFiles = Array.from(files)
    postFiles.forEach(file =&gt; &#123;
      if (!beforeUpload) &#123;
        postFileUpload(file)
      &#125; else &#123;
        const result = beforeUpload(file)
        if (result &amp;&amp; result instanceof Promise) &#123;
          result.then(...)
        &#125; else if (result !== false) &#123;
          postFileUpload(file)
        &#125;
      &#125;
    &#125;)
  &#125;

  const postFileUpload = () =&gt; &#123;&#125;

  &lt;input
    type=&quot;file&quot;
    style=&#123;&#123;display: 'none'&#125;&#125;
    onChange=&#123;handleChangeFile&#125;
    ref=&#123;fileInputRef&#125;
    accept=&#123;accept&#125;
    multiple=&#123;multiple&#125;
  /&gt;
  &lt;button onClick=&#123;handleClick&#125;&gt;Upload File&lt;/button&gt;
&#125;</code></pre>
<p>当我们点击<code>button</code>时触发<code>handleClick</code>选择上传文件, 上传时触发<code>uploadFiles</code>. 在<code>uploadFiles</code>中对于异步上传我们需要单独处理. 最后就是<code>ajax</code>上传了.</p>
<p>到此我们的<code>Upload</code>组件level已经上了档次了但还是不够, 如果用户想看见上传进度, 并且想看见上传的文件而且还能做移除操作就更完美了. 接下来我们继续完善!</p>
<h4 id="再上一个等圾"><a href="#再上一个等圾" class="headerlink" title="再上一个等圾"></a>再上一个等圾</h4><p>为了完成上面要求我们需要增加一些属性来作为支持.</p>
<pre><code>interface UploadFile &#123;
  ...
  raw: File;
  percent?: number;
  response?: any;
  error?: any
&#125;

interface UploadProps &#123;
  ...
  onRemove?: (file: UploadFile) =&gt; void;
  onProgress?: (percentage: number, file: UploadFile) =&gt; void;
&#125;

const Upload: React.FC&lt;UploadProps&gt; = (props) =&gt; &#123;
  const &#123;
    ...
    onRemove,
    onProgress,
    children
  &#125; = props

  // 展示的文件列表
  const [fileList, setFileList] = useState&lt;UploadFile&gt;(defaultFileList || [])

  const uploadFile = (files: FileList) =&gt; &#123;
    let postFiles = Array.from(files)
    postFiles.forEach(file =&gt; &#123;
      if (!beforeUpload) &#123;
        postFileUpload(file)
      &#125; else &#123;
        const result = beforeUpload(file)
        if (result &amp;&amp; result instanceof Promise) &#123;
          // 从beforeUplaod返回的数据中获取上传文件的详细信息
          result.then(processFile =&gt; &#123;
            postFileUpload(procesFile)
          &#125;)
        &#125; else if (result !== false) &#123;
          postFileUpload(file)
        &#125;
      &#125;
    &#125;)
  &#125;

  const postFileUpload = (file: UploadFile) =&gt; &#123;
    let _file: UploadFile = &#123;
      uid: Date.now() + &#39;upload_file&#39;,
      name: file.name,
      status: &#39;ready&#39;,
      size: file.size,
      percent: 0,
      raw: file
    &#125;

    const formData = new FormData()
    formData.append(name || &#39;file&#39;, file)
    if (data) &#123;
      Object.keys(data).forEach(key =&gt; &#123;
        formData.append(key, data[key])
      &#125;)
    &#125;

    axios.post(action, formData, &#123;
      onUploadProgress: (e) =&gt; &#123;
        // 上传进度百分比
        let percentage = Math.round((e.loaded * 100) / e.total) || 0
        if (percentage &lt; 100) &#123;
          ...
          if (onProgress) &#123;
            onProgress(percenage, file)
          &#125;
        &#125;
      &#125;
    &#125;).then(res =&gt; &#123;
      ...
      if (onSuccess) &#123;
        onSuccess(res.data, file)
      &#125;
      if (onChange) &#123;
        onChange(file)
      &#125;
    &#125;).catch(err =&gt; &#123;
      ...
      if (onError) &#123;
        onError(err, file)
      &#125;
      if (onChange) &#123;
        onChange(file)
      &#125;
    &#125;)
  &#125;
&#125;</code></pre>
<p>上面代码中我们从<code>beforeUpload</code>中获取到上传文件的详细信息作为上传时提交的参数, 比如<code>name, size</code>, 最后提交后<code>onUploadProgress</code>回调中会返回上传文件的进度, 我们根据上传进度来时时更新文件列表. 接下来完成文件列表的更新!</p>
<pre><code>...
cosnt postFileUpload = (file: UploadFile) =&gt; &#123;
  ...

  const setFileList = (prevFileList) =&gt; &#123;
    return [_file, ...prevFileList]
  &#125;

  axios.post(action, formData, &#123;
    onUploadProgress: e =&gt; &#123;
      ...
      if (percentage &lt; 100) &#123;
        uploadFileList(_file, &#123;
          percent: percentage,
          status: &#39;uploading&#39;
        &#125;)
        if (onProgress) &#123;
          onProgress(percentage, file)
        &#125;
      &#125;
    &#125;
  &#125;).then(res =&gt; &#123;
    uploadFileList(_file, &#123;
      response: res.data,
      status: &#39;success&#39;
    &#125;)
    if (onSuccess) &#123;
      onSuccess(res.data, file)
    &#125;
    ...
  &#125;).catch(err =&gt; &#123;
    uploadFileList(_file, &#123;
      error: err,
      status: &#39;error&#39;
    &#125;)
    ...
  &#125;)
&#125;
const uploadFileList = (updateFile: UploadFile, updateObj: Partial&lt;UploadFile&gt;) =&gt; &#123;
  setFileList(prevFileList =&gt; &#123;
    return prevFileList.map(file =&gt; &#123;
      if (file.uid === updateFile.uid) &#123;
        return &#123;...file, ...updateObj&#125;
      &#125; else &#123;
        return file
      &#125;
    &#125;)
  &#125;)
&#125;

const handleRemove = (file: UploadFile) =&gt; &#123;
  setFileList(prevFileList =&gt; &#123;
    return prevFileList.filter(item =&gt; &#123;
      return item.uid !== file.uid 
    &#125;)
  &#125;)
  if (onRemove) onRemove(file)
&#125;

...
return (
  ...
  &lt;UploadList fileList=&#123;fileList&#125; onRemove=&#123;handleRemove&#125;&gt;
)
</code></pre>
<p>到此我们的上传进度和移除文件基本完成. 接下来为了减少用户使用时手动增加配置, 我们增加一些配置, 比如axios提交时是否设置<code>headers</code>, 是否需要配置<code>withCredentials</code>允许跨站点请求</p>
<pre><code>interface UploadProps &#123;
  headers?: &#123;[key: string]: any&#125;;
  withCredentials?: boolean;
&#125;

const Upload: React.FC&lt;UploadProps&gt; = (props) =&gt; &#123;
  const &#123;..., headers, withCredentials&#125; = props
&#125;

cosnt postFileUpload = (file: UploadFile) =&gt; &#123;
  axois.post(_, _, &#123;
    headers: &#123;
      ...headers,
      &#39;Content-Type&#39;: &#39;multipart/form-data&#39;
    &#125;,
    withCredentials,
    onUploadProgress: (e) =&gt; &#123;...&#125;
  &#125;)
&#125;</code></pre>
<p>目前我们的<code>Uplaod</code>组件以及完成百分之八十了, 本着一切为用户服务的思想, 我们在此基础上提供一个新的功能—拖拽, 让用户使用更加方便.</p>
<h4 id="拖拽上传"><a href="#拖拽上传" class="headerlink" title="拖拽上传"></a>拖拽上传</h4><p>为了实现拖拽功能我们需要新增拖拽属性<code>drag</code></p>
<pre><code>interface UploadProps &#123;
  ...
  drag?: boolean;
&#125;

interface DraggerProps &#123;
  onFile: (file: FileList) =&gt; void;
&#125;

const Upload: React.FC&lt;UploadProps&gt; = (props) =&gt; &#123;
  const &#123;..., drag&#125; = props

  return (
    ...
    &#123;
      drag ? &lt;Dragger onFile=&#123;files =&gt; &#123;uploadFiles(file)&#125;&#125;&gt;&#123;children&#125;&lt;/Dragger&gt;
    &#125;
  )
&#125;</code></pre>
<p>我们将拖拽功能单独提取出来写在<code>Dragger</code>组件中</p>
<pre><code>const Dragger: React.FC&lt;DraggerProps&gt; = (props) =&gt; &#123;
  const &#123;onFile, children&#125; = props

  const [dragOver, setDragOver] = useState(false)
  const dragClass = classNames(&#39;r-upload-drag&#39;, &#123;
    &#39;is-dragover&#39;: dragOver
  &#125;)

  const handleDrap = (e: DragEvent&lt;HTMLElement&gt;) =&gt; &#123;
    e.preventDefault()
    setDragOver(false)
    onFile(e.dataTransfer.files)
  &#125;

  const handleDrag = (e: DragEvent&lt;HTMLElement&gt;, over: boolean) =&gt; &#123;
    e.preventDefault()
    setDragOver(over)
  &#125;

  return (
    &lt;div
      className=&#123;dragClass&#125;
      onDragOver=&#123;e =&gt; &#123;handleDrag(e, true)&#125;&#125;
      onDragLeave=&#123;e =&gt; &#123;handleDrag(e, false)&#125;&#125;
      onDrap=&#123;handleDrap&#125;
    &gt;&#123;children&#125;&lt;/div&gt;
  )
&#125;</code></pre>
<p>最后我们对<code>Upload</code>组件做个善后工作, 我们去掉<code>button</code>还是用<code>input</code>来做上传</p>
<pre><code>const Upload: React.FC&lt;UploadProps&gt; = (props) =&gt; &#123;
  ...
  return (
    &lt;div className=&quot;r-upload-component&quot;&gt;
      &lt;div
        className=&quot;r-upload-input&quot;
        style=&#123;&#123;display: "inline-block"&#125;&#125;
        onClick=&#123;handleClick&#125;
      &gt;
        &#123;
          drag ? &lt;Dragger onFile=&#123;files =&gt; &#123;uploadFiles(file)&#125;&#125;&gt;&#123;children&#125;&lt;/Dragger&gt;
        &#125;
        &lt;input
          type=&quot;file&quot;
          className=&quot;r-file-input&quot;
          style=&#123;&#123;display: 'none'&#125;&#125;
          onChange=&#123;handleChangeFile&#125;
          ref=&#123;fileInputRef&#125;
          accept=&#123;accept&#125;
          multiple=&#123;multiple&#125;
        /&gt;
      &lt;/div&gt;
      &lt;UploadList fileList=&#123;fileList&#125; onRemove=&#123;handleRemove&#125; /&gt;
    &lt;/div&gt;
  )
&#125;</code></pre>
<p><code>UploadList</code>组件就是上传展示给用户的文件列表, 我们会根据上传不同的状态进行展示</p>
<pre><code>const UploadList: React.FC&lt;UploadListProps&gt; = (props) =&gt; &#123;
  const &#123;fileList, onRemove&#125; = props

  return (
    &lt;ul className=&quot;r-upload-list&gt;
      &#123;
        fileList.map(item =&gt; &#123;
          return (
            &lt;li className=&quot;r-upload-list-item&quot; key=&#123;item.uid&#125;&gt;
              &lt;span&gt;
                &lt;Icon icon=&quot;ion-alt&quot; /&gt;
                &#123;item.name&#125;
              &lt;/span&gt;
              &lt;span className=&quot;file-status&gt;
                &#123;
                  item.status === &#39;uploading&#39; &amp;&amp;
                  &lt;Icon icon=&quot;spinner&quot; spin theme=&quot;primary&quot; /&gt;
                &#125;
                &#123;
                  item.status === &#39;success&#39; &amp;&amp;
                  &lt;Icon icon=&quot;check-circle&quot; spin theme=&quot;success&quot; /&gt;
                &#125;
                &#123;
                  item.status === &#39;error&#39; &amp;&amp;
                  &lt;Icon icon=&quot;tims-circle&quot; spin theme=&quot;danger&quot; /&gt;
                &#125;
              &lt;/span&gt;
              &lt;span className=&quot;file-action&quot;&gt;
                &lt;Icon icon=&quot;times&quot; onClick=&#123;() =&gt; onRemove(item)&#125; /&gt;
              &lt;/span&gt;
              &#123;
                item.status === &#39;uploading&#39; &amp;&amp; &lt;Progress percent=&#123;item.percent || 0&#125; /&gt;
              &#125;
            &lt;/li&gt;
          )
        &#125;)
      &#125;
    &lt;/ul&gt;
  )
&#125;</code></pre>
<p>ok, 一个具备点击, 拖拽上传的<code>Uplaod</code>组件就此完成了, 回顾开始我们想要完成一个类似与Ant Design React/Vue组件库<code>Upload</code>组件大致相同的上传组件, 我们根据上传组件所需的功能将其进行细化拆分最后用代码一步步实现, 虽然组件我们编写完成了, 但一个完整的组件不单单是实现其功能就算完成, 下一步我们将为其编写单元测试来测试我们的上传组件是否真是可用.</p>

        
    

    
</div>


                

                <!-- Post Comments -->
                
                    
    <!-- 使用 changyan -->
<div id="changyan-comment">
    <!--PC和WAP自适应版-->
<div id="SOHUCS" sid="2020/11/03/uploadComponent/"  ></div>
<script type="text/javascript">
(function(){
var appid = 'cyv74isYm';
var conf = '315a48df5a37b43bb5aa8f6dbc43ac3c';
var width = window.innerWidth || document.documentElement.clientWidth;
if (width < 960) {
window.document.write('<script id="changyan_mobile_js" charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/mobile/wap-js/changyan_mobile.js?client_id=' + appid + '&conf=' + conf + '"><\/script>'); } else { var loadJs=function(d,a){var c=document.getElementsByTagName("head")[0]||document.head||document.documentElement;var b=document.createElement("script");b.setAttribute("type","text/javascript");b.setAttribute("charset","UTF-8");b.setAttribute("src",d);if(typeof a==="function"){if(window.attachEvent){b.onreadystatechange=function(){var e=b.readyState;if(e==="loaded"||e==="complete"){b.onreadystatechange=null;a()}}}else{b.onload=a}}c.appendChild(b)};loadJs("https://changyan.sohu.com/upload/changyan.js",function(){window.changyan.api.config({appid:appid,conf:conf})}); } })(); </script>

</div>
<style>
    #changyan-comment{
        background-color: #eee;
        padding: 2pc;
    }
</style>

                
            </div>

            <!-- Post Prev & Next Nav -->
            <nav class="material-nav mdl-color-text--grey-50 mdl-cell mdl-cell--12-col">
    <!-- Prev Nav -->
    
        <a href="/labike/2020/11/04/UploadComponentTest/" id="post_nav-newer" class="prev-content">
            <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon mdl-color--white mdl-color-text--grey-900" role="presentation">
                <i class="material-icons">arrow_back</i>
            </button>
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            新篇
        </a>
    

    <!-- Section Spacer -->
    <div class="section-spacer"></div>

    <!-- Next Nav -->
    
        <a href="/labike/2020/11/03/reveserLinkList/" id="post_nav-older" class="next-content">
            旧篇
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon mdl-color--white mdl-color-text--grey-900" role="presentation">
                <i class="material-icons">arrow_forward</i>
            </button>
        </a>
    
</nav>

        </div>
    </div>



                    
                        <!-- Overlay For Active Sidebar -->
<div class="sidebar-overlay"></div>

<!-- Material sidebar -->
<aside id="sidebar" class="sidebar sidebar-colored sidebar-fixed-left" role="navigation">
    <div id="sidebar-main">
        <!-- Sidebar Header -->
        <div class="sidebar-header header-cover" style="background-image: url(/labike/img/sidebar_header.png);">
    <!-- Top bar -->
    <div class="top-bar"></div>

    <!-- Sidebar toggle button -->
    <button type="button" class="sidebar-toggle mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--icon" style="display: initial;" data-upgraded=",MaterialButton,MaterialRipple">
        <i class="material-icons">clear_all</i>
        <span class="mdl-button__ripple-container">
            <span class="mdl-ripple">
            </span>
        </span>
    </button>

    <!-- Sidebar Avatar -->
    <div class="sidebar-image">
        <img src="/labike/img/avatar.png" alt="labike's avatar">
    </div>

    <!-- Sidebar Email -->
    <a data-toggle="dropdown" class="sidebar-brand" href="#settings-dropdown">
        ddmmy@hotmail.com
        <b class="caret"></b>
    </a>
</div>


        <!-- Sidebar Navigation  -->
        <ul class="nav sidebar-nav">
    <!-- User dropdown  -->
    <li class="dropdown">
        <ul id="settings-dropdown" class="dropdown-menu">
            
                <li>
                    <a href="mailto: ddmmy@hotmail.com" target="_blank" title="Email Me">
                        
                            <i class="material-icons sidebar-material-icons sidebar-indent-left1pc-element">email</i>
                        
                        Email Me
                    </a>
                </li>
            
        </ul>
    </li>

    <!-- Homepage -->
    
        <li id="sidebar-first-li">
            <a href="/labike/">
                
                    <i class="material-icons sidebar-material-icons">home</i>
                
                主页
            </a>
        </li>
        
    

    <!-- Archives  -->
    
        <li class="dropdown">
            <a href="#" class="ripple-effect dropdown-toggle" data-toggle="dropdown">
                
                    <i class="material-icons sidebar-material-icons">inbox</i>
                
                    归档
                <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
            <li>
                <a class="sidebar_archives-link" href="/labike/archives/2021/05/">五月 2021<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/labike/archives/2021/04/">四月 2021<span class="sidebar_archives-count">2</span></a></li><li><a class="sidebar_archives-link" href="/labike/archives/2021/03/">三月 2021<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/labike/archives/2021/02/">二月 2021<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/labike/archives/2020/12/">十二月 2020<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/labike/archives/2020/11/">十一月 2020<span class="sidebar_archives-count">24</span></a></li><li><a class="sidebar_archives-link" href="/labike/archives/2020/10/">十月 2020<span class="sidebar_archives-count">9</span></a>
            </ul>
        </li>
        
    

    <!-- Categories  -->
    
        <li class="dropdown">
            <a href="#" class="ripple-effect dropdown-toggle" data-toggle="dropdown">
                
                    <i class="material-icons sidebar-material-icons">chrome_reader_mode</i>
                
                分类
                <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
                <li>
                <a class="sidebar_archives-link" href="/labike/categories/%E5%85%B6%E4%BB%96/">其他<span class="sidebar_archives-count">1</span></a></li><li><a class="sidebar_archives-link" href="/labike/categories/%E5%89%8D%E7%AB%AF/">前端<span class="sidebar_archives-count">9</span></a></li><li><a class="sidebar_archives-link" href="/labike/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构<span class="sidebar_archives-count">10</span></a></li><li><a class="sidebar_archives-link" href="/labike/categories/%E7%AE%97%E6%B3%95/">算法<span class="sidebar_archives-count">19</span></a>
            </ul>
        </li>
        
    

    <!-- Pages  -->
    
        <li>
            <a href="/links" title="友情链接">
                
                    <i class="material-icons sidebar-material-icons">face</i>
                
                友情链接
            </a>
        </li>
        
    
        <li>
            <a href="/tags" title="标签">
                
                    <i class="material-icons sidebar-material-icons">cloud_circle</i>
                
                标签
            </a>
        </li>
        
    

    <!-- Article Number  -->
    
</ul>


        <!-- Sidebar Footer -->
        <!--
I'm glad you use this theme, the development is no so easy, I hope you can keep the copyright, I will thank you so much.
If you still want to delete the copyrights, could you still retain the first one? Which namely "Theme Material"
It will not impact the appearance and can give developers a lot of support :)

很高兴您使用并喜欢该主题，开发不易 十分谢谢与希望您可以保留一下版权声明。
如果您仍然想删除的话 能否只保留第一项呢？即 "Theme Material"
它不会影响美观并可以给开发者很大的支持和动力。 :)
-->

<!-- Sidebar Divider -->

    <div class="sidebar-divider"></div>


<!-- Theme Material -->

    <a href="https://github.com/viosey/hexo-theme-material"  class="sidebar-footer-text-a" target="_blank">
        <div class="sidebar-text mdl-button mdl-js-button mdl-js-ripple-effect sidebar-footer-text-div" data-upgraded=",MaterialButton,MaterialRipple">
            主题 - Material
            <span class="sidebar-badge badge-circle">i</span>
        </div>
    </a>


<!-- Help & Support -->
<!--

-->

<!-- Feedback -->
<!--

-->

<!-- About Theme -->
<!--

-->

    </div>

    <!-- Sidebar Image -->
    

</aside>

                    

                    
                        <!-- Footer Top Button -->
                        <div id="back-to-top" class="toTop-wrap">
    <a href="#top" class="toTop">
        <i class="material-icons footer_top-i">expand_less</i>
    </a>
</div>

                    

                    <!--Footer-->
<footer class="mdl-mini-footer" id="bottom">
    
        <!-- Paradox Footer Left Section -->
        <div class="mdl-mini-footer--left-section sns-list">
    <!-- Twitter -->
    

    <!-- Facebook -->
    

    <!-- Google + -->
    

    <!-- Weibo -->
    

    <!-- Instagram -->
    

    <!-- Tumblr -->
    

    <!-- Github -->
    

    <!-- LinkedIn -->
    

    <!-- Zhihu -->
    

    <!-- Bilibili -->
    

    <!-- Telegram -->
    
    
    <!-- V2EX -->
    
</div>

        
        <!--Copyright-->
        <div id="copyright">
            <a style="text-decoration: none;" target="_blank" rel="noopener" href="https://beian.miit.gov.cn">陕ICP备17012824号-1</a>
            Copyright&nbsp;©&nbsp;2018&nbsp;-<script type="text/javascript">var fd = new Date();document.write("&nbsp;" + fd.getFullYear() + "&nbsp;");</script>春日游, 杏花吹满头
            
        </div>

        <!-- Paradox Footer Right Section -->

        <!--
        I am glad you use this theme, the development is no so easy, I hope you can keep the copyright.
        It will not impact the appearance and can give developers a lot of support :)

        很高兴您使用该主题，开发不易，希望您可以保留一下版权声明。
        它不会影响美观并可以给开发者很大的支持。 :)
        -->

        <div class="mdl-mini-footer--right-section">
            <div>
                <div class="footer-develop-div">Powered by <a href="https://hexo.io" target="_blank" class="footer-develop-a">Hexo</a></div>
                <div class="footer-develop-div">Theme - <a href="https://github.com/viosey/hexo-theme-material" target="_blank" class="footer-develop-a">Material</a></div>
            </div>
        </div>
    
</footer>


                    <!-- Import JS File -->

    <script>lsloader.load("lazyload_js","/labike/js/lazyload.min.js?wgjW/HuQG9JDgvPDPoRAng==", true)</script>



    <script>lsloader.load("js_js","/labike/js/js.min.js?d+XW6aNuIPgOn1iA7NnB4g==", true)</script>



    <script>lsloader.load("np_js","/labike/js/nprogress.js?pl3Qhb9lvqR1FlyLUna1Yw==", true)</script>


<script type="text/ls-javascript" id="NProgress-script">
    NProgress.configure({
        showSpinner: true
    });
    NProgress.start();
    $('#nprogress .bar').css({
        'background': '#29d'
    });
    $('#nprogress .peg').css({
        'box-shadow': '0 0 10px #29d, 0 0 15px #29d'
    });
    $('#nprogress .spinner-icon').css({
        'border-top-color': '#29d',
        'border-left-color': '#29d'
    });
    setTimeout(function() {
        NProgress.done();
        $('.fade').removeClass('out');
    }, 800);
</script>









   <!-- 畅言公共 js 代码 start -->
<script id="cy_cmt_num" src="https://changyan.sohu.com/upload/plugins/plugins.list.count.js?clientId=cyv74isYm">
</script>
<!-- 畅言公共 js 代码 end -->





<!-- UC Browser Compatible -->
<script>
	var agent = navigator.userAgent.toLowerCase();
	if(agent.indexOf('ucbrowser')>0) {
		document.write('
<link rel="stylesheet" href="/labike/css/uc.css">
');
	   alert('由于 UC 浏览器使用极旧的内核，而本网站使用了一些新的特性。\n为了您能更好的浏览，推荐使用 Chrome 或 Firefox 浏览器。');
	}
</script>

<!-- Import prettify js  -->

    
        
            <script>lsloader.load("prettify_js","/labike/js/prettify.min.js?WN07fivHQSMKWy7BmHBB6w==", true)</script>
        
    



<!-- Window Load -->
<!-- add class for prettify -->
<script type="text/ls-javascript" id="window-load">
    $(window).on('load', function() {
        // Post_Toc parent position fixed
        $('.post-toc-wrap').parent('.mdl-menu__container').css('position', 'fixed');
    });

    
        
            $(function() {
                $('pre').addClass('prettyprint linenums').attr('style', 'overflow:auto;');
                prettyPrint();
                })
        
    
    
</script>

<!-- MathJax Load-->


<!-- Bing Background -->


<script type="text/ls-javascript" id="lazy-load">
    // Offer LazyLoad
    queue.offer(function(){
        $('.lazy').lazyload({
            effect : 'show'
        });
    });

    // Start Queue
    $(document).ready(function(){
        setInterval(function(){
            queue.execNext();
        },200);
    });
</script>

<!-- Custom Footer -->



<script>
    (function(){
        var scriptList = document.querySelectorAll('script[type="text/ls-javascript"]')

        for (var i = 0; i < scriptList.length; ++i) {
            var item = scriptList[i];
            lsloader.runInlineScript(item.id,item.id);
        }
    })()
console.log('\n %c © Material Theme | Version: 1.5.2 | https://github.com/viosey/hexo-theme-material %c \n', 'color:#455a64;background:#e0e0e0;padding:5px 0;border-top-left-radius:5px;border-bottom-left-radius:5px;', 'color:#455a64;background:#e0e0e0;padding:5px 0;border-top-right-radius:5px;border-bottom-right-radius:5px;');
</script>

                </main>
            </div>
        </body>
    
</html>
